#include "ex03.cpp"
#include <stdint.h>
#include <stdio.h>

pw::semaphore ready(0), sem1(0), sem2(0), resource(1);
bool stop = false;

// [[ex03ha]]
// DEBUG descr Split lock with the single-stepping API.
class splitlock
{
public:
	// @param safe - flag: if true, place the lock
	// initially into a safe state, so that the worker
	// thread may not enter it until the control
	// thread releases it; if false, place it into the
	// unsafe state and allow the worker thread to
	// enter it freely
	splitlock(bool safe = false) :
		cond_safe_(cond_enter_),
		may_enter_(!safe),
		is_safe_(safe),
		may_step_(false)
	{ }

	// Control thread side of API.

	void request_safe()
	{
		pw::lockmutex lm(cond_enter_);
		may_enter_ = false;
	}

	void wait_safe()
	{
		pw::lockmutex lm(cond_enter_);
		while (!is_safe_ || may_step_) {
			cond_safe_.wait();
		}
	}

	// A convenience combination - just in case
	void lock_safe()
	{
		request_safe();
		wait_safe();
	}

	void release_safe()
	{
		pw::lockmutex lm(cond_enter_);
		may_enter_ = true;
		cond_enter_.broadcast();
	}

	// Returns true if a thread was waiting and is now
	// allowed to step, or false if there was no
	// thread waiting.
	bool release_safe_step()
	{
		pw::lockmutex lm(cond_enter_);
		if (cond_enter_.sleepers_) {
			may_step_ = true;
			cond_enter_.broadcast();
			return true;
		} else {
			return false;
		}
	}

	// Worker thread side of API.

	void enter()
	{
		pw::lockmutex lm(cond_enter_);
		while (!may_enter_ && !may_step_)
			cond_enter_.wait();
		is_safe_ = false;
		may_step_ = false;
	}

	void leave()
	{
		pw::lockmutex lm(cond_enter_);
		is_safe_ = true;
		cond_safe_.broadcast();
	}

	// Try to enter the critical section.  Never
	// blocks.  Returns true on success, false if the
	// splitlock is already safe.
	bool try_enter()
	{
		pw::lockmutex lm(cond_enter_);
		if (!may_enter_ && !may_step_)
			return false; 
		is_safe_ = false;
		may_step_ = false;
		return true;
	}

	// After an enter attempt has failed and the
	// caller has freed all the sensitive resources,
	// block until the control thread releases the
	// lock and another attempt to enter may be made.
	void continue_enter()
	{
		pw::lockmutex lm(cond_enter_);
		while (!may_enter_ && !may_step_)
			cond_enter_.wait();
	}

protected:
	// signaled when the worker thread is allowed to enter
	pw::pmcond cond_enter_; 
	// signaled when the control thread becomes safe
	pw::pchaincond cond_safe_; 
	// flag: the worker thread may enter
	bool may_enter_; 
	// flag: the control thread is safe (working
	// thread has not entered)
	bool is_safe_; 
	// flag: the worker thread may enter once
	bool may_step_; 
};
// END

void ex03hb()
{
// worker copied from ex03gb, with added steps counter
class workerClass : public pw::pwthread
{
public:
	workerClass() :
		split_(true), steps_(0)
	{ }
	// ...
	void *execute()
	{
		bool done = false;
		// ... initialize ...
		ready.signal();
		while(!done)
		{
			// ... get next request ...
			// ... get the resources
			while (!split_.try_enter()) {
				// ... release the resources
				split_.continue_enter();
				// ... get the resources
			}
			// ... process the request ...
			++steps_;
			done = stop;
			split_.leave();
			// ... release the resources
		}
		return 0;
	}

public:
	splitlock split_;
	int steps_;
};
workerClass *worker_ptr = new workerClass;
worker_ptr->start();
ready.wait();

// [[ex03hb]]
worker_ptr->split_.release_safe_step();
worker_ptr->split_.wait_safe();
// END

// [[ex03hc]]
if (worker_ptr->split_.release_safe_step()) {
	worker_ptr->split_.wait_safe();
	printf("worker made a step\n");
	// DEBUG {
	printf("worker steps total %d\n", worker_ptr->steps_);
	// DEBUG }
} else {
	printf("worker is not ready to do the step\n");
}
// END

// DEBUG {
int nsteps = 3;
stop = true;
// DEBUG }
// [[ex03hd]]
for (int i = 0; i < nsteps; i++) {
	if (worker_ptr->split_.release_safe_step()) {
		worker_ptr->split_.wait_safe();
		printf("worker made a step\n");
	// DEBUG {
		printf("worker steps total %d\n", worker_ptr->steps_);
	// DEBUG }
	} else {
		printf("worker is not ready to do the step\n");
		break;
	}
}
// END
// DEBUG {
worker_ptr->join();
delete worker_ptr;
// DEBUG }
}
// DEBUG {
int main()
{
	ex03hb();
	return 0;
}
// DEBUG }
/* Sample output:

worker made a step
worker steps total 2
worker made a step
worker steps total 3
worker is not ready to do the step

*/
